﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Oplaco.Core.BusinessLogicLayer;
using Oplaco.Core.DAL;
using System.Data.SqlClient;


namespace Oplaco.Web.DTO
{

    /// <summary>
    /// Near future want move to a facade,....
    /// </summary>
    public static class CategoryManager
    {            

        private static readonly string[] AllCategoriesKeyArrayWrap = new string[] { AllCategoriesKey };
        internal const string AllCategoriesKey = "CategoryCache_AllCategoriesKey";

        #region Public

        /// <summary>
        /// Adds category to database
        /// </summary>
        /// <param name="parentId"></param>
        /// <param name="title"></param>
        public static void Add(int parentId, string title) {
            try
            {
                Oplaco.Core.BusinessLogicLayer.CategoriesDB.InsertCategory(parentId, title);
            }
            catch (SqlException SQLEX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyDataAccessLayerException(SQLEX);
            }
            catch (Exception EX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyBusinessLayerException(EX);
            }
        }
        
        /// <summary>
        /// Removes category from database
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool Remove(int id) {

            if (CategoriesDB.RemoveCategory(id))
            {
                CategoryManager.ClearCache(HttpContext.Current);
                return true;
            }
            return false;
        }
        
        /// <summary>
        /// Updates the database
        /// </summary>
        /// <param name="id"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        public static bool Update(int id, string title) {
            try
            {
                CategoriesDB.UpdateCategoryName(id, title);
                CategoryManager.ClearCache(HttpContext.Current);
                return true;
            }
            catch (SqlException SQLEX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyDataAccessLayerException(SQLEX);
            }
            catch (Exception EX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyBusinessLayerException(EX);
            }
            return false;
        
        }


        /// <summary>
        /// Fetches the Parent from cache, child titles as a comma sep. string
        /// </summary>
        /// <param name="context"></param>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        /// <remarks>Not my favorite solution, when we move to a new dal, this should be solved</remarks>
        public static string GetCategoryPath(HttpContext context, int categoryId)
        {
            String result = String.Empty;
            CachedCategoryCollection allcategories = CategoryManager.Fetch(context, 100); //basically the context, should not be null, but we try to give result anyway
                        
            if (allcategories == null) return String.Empty;

            var query = from c in allcategories where c.Id == categoryId select c;
            if (query!=null && query.Count() > 0)
            {
                result = query.Single().Title;
            }
            else {
                var querychild = from c in allcategories
                                 where c.HasChilds
                                 select c.ChildCollection into x
                                 from cc in x
                                 where cc.Id == categoryId
                                 select cc;
                if (querychild != null && querychild.Count() > 0) {
                    result = (from c in allcategories where c.Id == querychild.Single().ParentId select c.Title).Single<String>() +", "+ querychild.Single().Title;
                }
            }
            return result;
        }

       
        /// <summary>
        /// Tries to find one item in the cached collection, when not found, it queries the database
        /// </summary>
        /// <param name="context"></param>
        /// <param name="categoryId"></param>
        /// <returns></returns>
        public static CachedCategory FetchSingle(int categoryId)
        {
            CachedCategory item = null;
            try
            {                                  
                                  
                CategoriesDataComponent.CategoriesDataTable result = CategoriesDB.GetParentCategoriesById(categoryId);
                    
                //selected category is parent
                if (result.Count == 1) {
                    item = (from CategoriesDataComponent.CategoriesRow c in result select new CachedCategory(c.Name, c.Id, 0, c.NumActiveAds, CategoryManager.FetchSubCategories(c.Id, 100))).Single<CachedCategory>();
                }
                //selected category is child
                if (result.Count == 2)
                {
                    item = new CachedCategory(result[1].Name, result[1].Id, result[1].ParentCategoryId, result[1].NumActiveAds, null);
                }
                    
                
                return item;
            }
            catch (SqlException SQLEX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyDataAccessLayerException(SQLEX);
            }
            catch (Exception EX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyBusinessLayerException(EX);
            }
            return null;
        }

        /// <summary>
        /// Fetches categories from database or cache
        /// </summary>
        /// <param name="context"></param>
        /// <param name="maximum"></param>
        /// <returns></returns>
        public static CachedCategoryCollection Fetch(HttpContext currentContext, int maximum) {

            CachedCategoryCollection allCategories = null;
            DateTime expiration = DateTime.Now.AddDays(1);
            try
            {
                if(currentContext!=null) allCategories = currentContext.Cache[AllCategoriesKey] as CachedCategoryCollection;

                if (allCategories == null)
                {
                    allCategories = new CachedCategoryCollection();
                    using (CategoriesDB db = new CategoriesDB())
                    {

                        CategoriesDataComponent.CategoriesDataTable result = db.GetCategoriesByParentId(0); 
                        CachedCategory[] query = (from CategoriesDataComponent.CategoriesRow c in result select new CachedCategory(c.Name, c.Id, 0, c.NumActiveAds, CategoryManager.FetchSubCategories(c.Id, 100))).ToArray<CachedCategory>();

                        if (query != null && query.Count() > 0)
                        {
                            allCategories.CopyTo(query, 0);
                            if(currentContext!=null) currentContext.Cache.Add(AllCategoriesKey, allCategories, null, expiration, TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null); //support cacheless operations as well
                            return allCategories;
                        }
                    }

                }
                else
                {
                    return allCategories;
                }
            }
            catch (SqlException SQLEX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyDataAccessLayerException(SQLEX);
            }
            catch (Exception EX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyBusinessLayerException(EX);
            }

            
            return null;
        }


        /// <summary>
        /// Fecthes categories from cache or database based on parent id
        /// </summary>
        /// <param name="context"></param>
        /// <param name="maximum"></param>
        /// <param name="parentId"></param>
        /// <returns></returns>
        public static CachedCategoryCollection FetchByParentId(HttpContext currentContext, int maximum, int parentId)
        {
             
            DateTime expiration = DateTime.Now.AddDays(1);
            try
            {                
                CachedCategoryCollection childCategories = childCategories = new CachedCategoryCollection(); //the cache is build up with all the categories, we do not want to overwrite those!
                CachedCategoryCollection allCategories = CategoryManager.Fetch(currentContext, maximum);

                if (allCategories == null) return null;

                if (parentId > 0)
                {
                    CachedCategory[] query = (from item in allCategories
                                              where item.HasChilds == true
                                              select item.ChildCollection into x
                                              from child in x
                                              where child.ParentId == parentId
                                              select child).ToArray<CachedCategory>();
                    if (query != null && query.Count() > 0)
                    {
                        childCategories.CopyTo(query, 0); //the cache is build up with all the categories, we do not want to overwrite those!                            
                        return childCategories;
                    }
                }

                
            }
            catch (SqlException SQLEX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyDataAccessLayerException(SQLEX);
            }
            catch (Exception EX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyBusinessLayerException(EX);
            }

            return null;
        }
        
        /// <summary>
        /// Clears the categories from cache
        /// </summary>
        public static void ClearCache(HttpContext context) {
            if (context != null) context.Cache.Remove(AllCategoriesKey);
        }


        /// <summary>
        /// Gets categories based on parentid
        /// </summary>
        /// <param name="parentId"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static CachedCategoryCollection FetchSubCategories(int parentId, int max) {

            try
            {

                CachedCategoryCollection allChildCategories = new CachedCategoryCollection();
                using (CategoriesDB db = new CategoriesDB())
                {
                    CategoriesDataComponent.CategoriesDataTable result = db.GetCategoriesByParentId(parentId);
                    CachedCategory[] query = (from CategoriesDataComponent.CategoriesRow c in result select new CachedCategory(c.Name, c.Id, parentId, c.NumActiveAds, null)).ToArray<CachedCategory>();

                    if (query != null && query.Count() > 0)
                    {
                        allChildCategories.CopyTo(query, 0);
                        return allChildCategories;
                    }
                }


            }
            catch (SqlException SQLEX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyDataAccessLayerException(SQLEX);
            }
            catch (Exception EX)
            {
                Oplaco.Exceptions.AppService.ProcessAndNotifyBusinessLayerException(EX);
            }
            return null;
        }

        #endregion
    }
}
